/*
 * Copyright (c) 2025 Li Auto Inc. and its affiliates
 * Licensed under the Apache License, Version 2.0(the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* This file should never be included directly but, rather,
 * only indirectly through arch/chip/irq.h
 */

#ifndef __CHIPS_E3650_INCLUDE_UP_ATOMIC_H
#define __CHIPS_E3650_INCLUDE_UP_ATOMIC_H

#ifdef __cplusplus
extern "C" {
#endif

#include <Compiler.h>
#include <Platform_Types.h>

typedef volatile aligned_data(4) sint32 Atomic;

/*  \brief          原子加
 *  \param[in/out]  ptr     需要进行原子加的数据地址
 *  \param[in]      addVal  要加的数值
 *  \return         原子操作前的值
 */
static inline Atomic Os_AtomicAdd(Atomic *ptr, sint32 addVal)
{
    register sint32  preVal , val, status;

    __asm __volatile__(
        "1:ldrex    %0, [%3]\n"
        "add    %1, %0, %4\n"
        "strex    %2, %1, [%3]\n"
        "teq    %2, #0\n"
        "bne    1b\n"
        "DMB"
        : "=&r"(preVal), "=&r"(val), "=&r"(status)
        : "r"(ptr), "r"(addVal)
        : "cc", "memory");

    return preVal;
}

/*  \brief          原子减
 *  \param[in/out]  ptr     需要进行原子减的数据地址
 *  \param[in]      addVal  要减的数值
 *  \return         原子操作前的值
 */
static inline Atomic Os_AtomicSub(Atomic *ptr, sint32 subVal)
{
    register sint32  preVal , val, status;

    __asm __volatile__(
        "1:ldrex    %0, [%3]\n"
        "sub    %1, %0, %4\n"
        "strex    %2, %1, [%3]\n"
        "teq    %2, #0\n"
        "bne    1b\n"
        "DMB"
        : "=&r"(preVal), "=&r"(val), "=&r"(status)
        : "r"(ptr), "r"(subVal)
        : "cc", "memory");

    return preVal;
}

/*  \brief          原子递增
 *  \param[in/out]  ptr     需要进行原子递增的数据地址
 *  \return         原子操作前的值
 */
static inline Atomic Os_AtomicInc(Atomic *ptr)
{
    register sint32  preVal , val, status;

    __asm __volatile__(
        "1:ldrex   %0, [%3]\n"
        "add   %1, %0, #1\n"
        "strex   %2, %1, [%3]\n"
        "teq    %2, #0\n"
        "bne    1b\n"
        "DMB"
        : "=&r"(preVal), "=&r"(val), "=&r"(status)
        : "r"(ptr)
        : "cc", "memory");

    return preVal;
}

/*  \brief          原子递减
 *  \param[in/out]  ptr     需要进行原子减的数据地址
 *  \return         原子操作前的值
 */
static inline Atomic Os_AtomicDec(Atomic *ptr)
{
    register sint32  preVal , val, status;

    __asm __volatile__(
        "1:ldrex   %0, [%3]\n"
        "sub   %1, %0, #1\n"
        "strex   %2, %1, [%3]\n"
        "teq    %2, #0\n"
        "bne    1b\n"
        "DMB"
        : "=&r"(preVal), "=&r"(val), "=&r"(status)
        : "r"(ptr)
        : "cc", "memory");

    return preVal;
}

/*  \brief          原子或
 *  \param[in/out]  ptr     需要进行原子或的数据地址
 *  \param[in]      addVal  要或的数值
 *  \return         原子操作前的值
 */
static inline Atomic Os_AtomicOr(Atomic *ptr, sint32 orVal)
{
    register sint32 preVal , val, status;

    __asm __volatile__(
        "1:ldrex    %0, [%3]\n"
        "orr    %1, %0, %4\n"
        "strex    %2, %1, [%3]\n"
        "teq    %2, #0\n"
        "bne    1b\n"
        "DMB"
        : "=&r"(preVal), "=&r"(val), "=&r"(status)
        : "r"(ptr), "r"(orVal)
        : "cc", "memory");

    return preVal;
}

/*  \brief          原子异或
 *  \param[in/out]  ptr     需要进行原子异或的数据地址
 *  \param[in]      addVal  要异或的数值
 *  \return         原子操作前的值
 */
static inline Atomic Os_AtomicXor(Atomic *ptr, sint32 xorVal)
{
    register sint32  preVal , val, status;

    __asm __volatile__(
        "1:ldrex    %0, [%3]\n"
        "eor    %1, %0, %4\n"
        "strex    %2, %1, [%3]\n"
        "teq    %2, #0\n"
        "bne    1b\n"
        "DMB"
        : "=&r"(preVal), "=&r"(val), "=&r"(status)
        : "r"(ptr), "r"(xorVal)
        : "cc", "memory");

    return preVal;
}

/*  \brief          原子与
 *  \param[in/out]  ptr     需要进行原子与的数据地址
 *  \param[in]      addVal  要与的数值
 *  \return         原子操作前的值
 */
static inline Atomic Os_AtomicAnd(Atomic *ptr, sint32 andVal)
{
    register sint32  preVal , val, status;

    __asm __volatile__(
        "1:ldrex    %0, [%3]\n"
        "and    %1, %0, %4\n"
        "strex    %2, %1, [%3]\n"
        "teq    %2, #0\n"
        "bne    1b\n"
        "DMB"
        : "=&r"(preVal), "=&r"(val), "=&r"(status)
        : "r"(ptr), "r"(andVal));

    return preVal;
}

/*  \brief          原子与非
 *  \param[in/out]  ptr     需要进行原子与非的数据地址
 *  \param[in]      addVal  要与非的数值
 *  \return         原子操作前的值
 */
static inline Atomic Os_AtomicXand(Atomic *ptr, sint32 xandVal)
{
    register sint32  preVal , val, status;

    __asm __volatile__(
        "1:ldrex    %0, [%3]\n"
        "and    %1, %0, %4\n"
        "mvn   %1, %1\n"
        "strex    %2, %1, [%3]\n"
        "teq    %2, #0\n"
        "bne    1b\n"
        "DMB"
        : "=&r"(preVal), "=&r"(val), "=&r"(status)
        : "r"(ptr), "r"(xandVal)
        : "cc", "memory");

    return preVal;
}

/*  \brief          原子设值
 *  \param[in/out]  ptr     需要进行原子设值的数据地址
 *  \param[in]      addVal  要设值的数值
 *  \return         原子操作前的值
 */
static inline Atomic Os_AtomicSet(Atomic *ptr, sint32 setVal)
{
    register sint32  preVal , val, status;

    __asm __volatile__(
        "1:ldrex   %0, [%3]\n"
        "strex   %2, %4, [%3]\n"
        "teq    %2, #0\n"
        "bne    1b\n"
        "DMB"
        : "=&r"(preVal), "=&r"(val), "=&r"(status)
        : "r"(ptr), "r"(setVal)
        : "cc", "memory");

    return preVal;
}

/*  \brief          原子清值
 *  \param[in/out]  ptr     需要进行原子清值数据地址
 *  \param[in]      addVal  要清值的数值
 *  \return         原子操作前的值
 */
static inline Atomic Os_AtomicClean(Atomic *ptr)
{
    register sint32  preVal , val, status;

    __asm __volatile__(
        "1:ldrex    %0, [%3]\n"
        "mov    %1, #0\n"
        "strex    %2, %1, [%3]\n"
        "teq    %2, #0\n"
        "bne    1b\n"
        "DMB"
        : "=&r"(preVal), "=&r"(val), "=&r"(status)
        : "r"(ptr)
        : "cc", "memory");

    return preVal;
}

/*  \brief          原子比较交换
*   \details        比较*ptr与oldval的，如果两者相等，则将newval更新到*ptr并返回true,否则返回false
 *  \param[in/out]  ptr     需要进行原子比较交换的数据地址
 *  \param[in]      oldVal  旧值
 *  \param[in]      newVal  新值
 *  \return
 *                  true(1)    比较交换成功
 *                  false(0)   比较交换失败
 */
static inline sint32 Os_AtomicCmpSwap(Atomic *ptr, sint32 oldVal, sint32 newVal)
{
    register sint32 status, val, ret;

    __asm __volatile__(
        "1:ldrex    %1, [%3]\n"
        "teq    %1, %4\n"
        "bne    2f\n"
        "strex    %2, %5, [%3]\n"
        "teq    %2, #0\n"
        "bne    1b\n"
        "mov    %0, #1\n"
        "b    3f\n"
        "2:    clrex\n"
        "mov    %0, #0\n"
        "3:    \n"
        "DMB"
        : "=&r"(ret), "=&r"(val), "=&r"(status)
        : "r"(ptr), "r"(oldVal), "r"(newVal)
        : "cc", "memory");

    return ret;
}

#ifdef __cplusplus
}
#endif

#endif /* __CHIPS_E3650_INCLUDE_UP_ATOMIC_H */
